package com.situ.meetingRoom.util;

import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.sql.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/***
 * BaseDao => DBUtil
 */
public class DBUtil {
    public static final String DRIVER = "com.mysql.cj.jdbc.Driver";
    public static final String URL = "jdbc:mysql://127.0.0.1:3306/meeting?serverTimezone=GMT%2b8";
    public static final String USER = "root";
    public static final String PASSWORD = "238671";

    // 创建连接对象
    protected Connection getConn()throws ClassNotFoundException, SQLException {
        // 1. 加载驱动
        Class.forName(DRIVER);

        // 2. 创建连接对象
        return DriverManager.getConnection(URL, USER, PASSWORD);
    }
    // 关闭所有的资源
    protected void close(ResultSet rs, Statement stmt, Connection conn){
        try {
            if (rs != null) rs.close();
        } catch (SQLException e) {
            e.printStackTrace();
        }
        try {
            if (stmt != null) stmt.close();
        } catch (SQLException e) {
            e.printStackTrace();
        }
        try {
            if (conn != null) conn.close();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    // 创建通用的可以用于增删改的方法
    public int update(String sql, Object... params){
        Connection conn = null;
        PreparedStatement stmt = null;
        try {
            // 2. 创建连接对象
            conn = getConn();

            // 3. 创建执行对象
            stmt = conn.prepareStatement(sql);
            // 绑定参数
            for (int i=0; i<params.length; i++) {
                stmt.setObject(i+1, params[i]);
            }
            // 4. 执行SQL语句
            int res = stmt.executeUpdate();

            // 5. 处理返回结果
            return res;
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            close(null, stmt, conn);
        }

        return -1;
    }


    // 通用的查询操作
    // 为了能够适应不同的表，决定使用Map保存每一条记录
    public List<Map<String, Object>> query(String sql, Object... params){
        // 创建三个变量
        Connection conn = null;
        PreparedStatement stmt = null;
        ResultSet rs = null;
        List<Map<String, Object>> list = new ArrayList<Map<String, Object>>();

        //
        try {
            // 1.2. 创建连接对象
            conn = getConn();
            // 3. 创建执行对象
            stmt = conn.prepareStatement(sql);
            // 3.1 绑定参数
            for (int i=0; i<params.length; i++){
                stmt.setObject(i+1, params[i]);
            }

            // 4. 执行SQL语句
            rs = stmt.executeQuery();
            ResultSetMetaData metaData = rs.getMetaData();   // 获取元数据

            // 5. 处理返回结果
            while (rs.next()){
                // {}
                Map<String, Object> row = new HashMap<>();

                // 获取每一个列的值，通过字段下标
                for (int i=1; i<=metaData.getColumnCount(); i++){
                    row.put(StringUtil.change(metaData.getColumnLabel(i)), rs.getObject(i));
                }

                // 添加到集合中
                list.add(row);
            }
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            // 6. 关闭访问资源
            close(rs, stmt, conn);
        }
        return list;
    }

    // 只查询一条记录
    public Map<String, Object> queryOne(String sql, Object... params){
        List<Map<String, Object>> list = query(sql, params);    // 这里的可变参数没有问题
        if (list.size() == 1){
            return list.get(0);
        } else if (list.size() == 0){
            return null;
        } else {
            // 抛出异常
            throw new RuntimeException("你想查一条，结果查到了多条，看看哪里的问题吧！");
        }
    }

    // 使用泛型方法
    public <T> List<T> query(Class<T> cls, String sql, Object... params){
        // 现在就是下午
        List<Map<String, Object>> list = query(sql, params);
        List<T> resList = new ArrayList<>();

        for (Map<String, Object> row : list){
            T obj = mapToObject(cls, row);
            resList.add(obj);
        }

        return resList;
    }
    // 只查询一条记录
    public <T> T queryOne(Class<T> cls, String sql, Object... params){
        Map<String, Object> row = queryOne(sql, params);
        if (row != null){
            return mapToObject(cls, row);
        } else {
            return null;
        }
    }

    /**
     * 可以将一个Map对象，转换成指定类型的对象
     */
    private <T> T mapToObject(Class<T> cls, Map<String, Object> map){
        // 创建一个T类型对象, 反射
        T obj = null;
        try {
            // 利用反射，创建T类型的对象， T类应该有一个无参构造器
            obj = cls.newInstance();
            // map {sid: 1, sname: tom, ssex: 男, sphone: 13312341111, cid: 1}
            // 还需要设置值  - 反射有一些不方便， 使用内省
            // JavaBean由三个核心元素：属性、行为、事件
            BeanInfo beanInfo = Introspector.getBeanInfo(cls);
            // 获取所有的属性描述信息（包含读写方法）   至少要有一个get或set方法
            PropertyDescriptor[] properties = beanInfo.getPropertyDescriptors();
            for ( PropertyDescriptor property : properties) {
                // property.getName();
                String propertyName = property.getDisplayName();
                Object value = map.get(propertyName);
                Method setter = property.getWriteMethod();
                if (setter != null && value != null){
                    try {
                        // 调用obj的setXXX的方法，设置为value的值
                        setter.invoke(obj, value);
                    } catch (InvocationTargetException e) {
                        throw new RuntimeException(e);
                    } catch (IllegalAccessException e){
                        throw new RuntimeException(e);
                    } catch (IllegalArgumentException e){
                        throw new RuntimeException(e);
                    }
                }
            }

        } catch (InstantiationException e) {
            throw new RuntimeException(e);
        } catch (IllegalAccessException e) {
            throw new RuntimeException(e);
        } catch (IntrospectionException e) {
            throw new RuntimeException(e);
        }
        return obj;
    }
}
